/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Interface;

import Application.AlgorithmeDeDifficulte;
import Application.Deplacement;
import Application.Donnee;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;

/**
 * Classe monde contenant tous les éléments physiques du jeu 
 * (ovnis, projectiles, canon, etc.) et les méthodes agissant directement sur ceux-ci.
 * @author Laurent/Pascal
 * 
 */
public class Monde extends JPanel {

    final int longeurCanon = 129, vitesseProjectiles = 37, longueurTourelles = 80;
    public final int frequenceOvniDefault = 100, frequenceSuperOvniDefault = 200, frequenceBibitteDefault = 150, moduloPowerUpDefault = 5;
    public int frequenceOvni = 100, frequenceSuperOvni = 200, frequenceBibitte = 150, moduloPowerUp = 5;
    int compteurBibittes = 0, pointage = 0, compteurTimer = 0, compteurTimerTimeStop = 0;
    public String dernierAchievement = "";
    boolean prototype = false, difficulteAugmente = false, partieTerminee = false, animation = false, animationCommence = false;
    Canon canon = new Canon(prototype);
    Jeu jeu;
    double xSouris, ySouris;
    Deplacement deplacement = new Deplacement();
    Donnee donnees = new Donnee();
    MouseMotionListener sourisBouge;
    MouseListener sourisClique;
    double xCibleCanon, yCibleCanon;
    Random rnd = new Random();
    ArrayList<Ovni> ovnis = new ArrayList<Ovni>();
    ArrayList<Projectile> projectiles = new ArrayList<Projectile>();
    ArrayList<Bibitte> bibittes = new ArrayList<Bibitte>();
    ArrayList<Obus> obus = new ArrayList<Obus>();
    ArrayList<PowerUp> powerUps = new ArrayList<PowerUp>();
    volatile ArrayList<ElementDuJeu> aEnlever = new ArrayList<ElementDuJeu>();
    static ArrayList<Tourelle> tourelles = new ArrayList<Tourelle>();
    volatile ArrayList<Explosion> explosions = new ArrayList<Explosion>();
    ArrayList<ElementDuJeu> cibles = new ArrayList<ElementDuJeu>();
    boolean testPowerUp = true, sourisOn = true, enGoreMode=false;
    PowerUp puissanceEnAttente = null;
    curseurSpecial leCurseur =new curseurSpecial();
    static int killCount=1, powerUpsUtilise=0, killMineurs=0;

    AlgorithmeDeDifficulte algorithmeDeDifficulte = new AlgorithmeDeDifficulte(this);



    // <editor-fold defaultstate="collapsed" desc="Thread et timers">
    Thread threadExplosions = new Thread() {

        /**
         * Augmente la taille des explosions présentes dans le monde et les 
         * supprime lorsqu'ils ont atteint leur taille maximale.
         */
        @Override
        public synchronized void run() {

            while (true){

                for (int j = 0; j < explosions.size(); j++) {
                    if (explosions.get(j).tailleCercle * 2 < explosions.get(j).getHeight()) {
                        explosions.get(j).tailleCercle += 2;

                        if (explosions.get(j).tailleCercle * 2 > explosions.get(j).getHeight()) {
                            explosions.get(j).carre = true;
                        } else if (explosions.get(j).tailleCercle * 3 > explosions.get(j).getHeight()) {
                            explosions.get(j).couleur = Color.RED;
                            explosions.get(j).carre = false;
                        } else if (explosions.get(j).tailleCercle * 4 > explosions.get(j).getHeight()) {
                            explosions.get(j).carre = true;
                        } else if (explosions.get(j).tailleCercle * 6 > explosions.get(j).getHeight()) {
                            explosions.get(j).couleur = Color.ORANGE;
                            explosions.get(j).carre = false;
                        } else if (explosions.get(j).tailleCercle * 8 > explosions.get(j).getHeight()) {
                            explosions.get(j).carre = true;
                        }

                    } else {
                        remove(explosions.get(j));
                        aEnlever.add(explosions.get(j));
                    }
                }
               explosions.removeAll(aEnlever);



                repaint();
                revalidate();
                try {
                    Thread.sleep(13);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                    System.exit(1);
                }

            }}
        
    };
    Thread animationDeFin = new Thread() {

        ArrayList<Bibitte> bibittesATerre;

        /**
         * Lorsque la partie est finie, fait une animation dans laquelle
         * les bibittes s'empilent autour du canon pour le dévorer.
         */
        @Override
        public synchronized void run() {
            
            while (animation) {
                bibittesATerre = new ArrayList<Bibitte>();
                for (int bibitteATerre = 0; bibitteATerre < bibittes.size(); bibitteATerre++) {
                    if (bibittes.get(bibitteATerre).aTerre) {
                        bibittesATerre.add(bibittes.get(bibitteATerre));
                    }

                }
                boolean tousEmpiles = true;
                for (int i = 0; i < bibittesATerre.size(); i++) {
                    if (!bibittesATerre.get(i).empile) {
                        tousEmpiles = false;
                    }
                }
                if (tousEmpiles == false) {
                    for (int i = 0; i < bibittesATerre.size(); i++) {
                        if (bibittesATerre.get(i).getX() == canon.xDebutCanon - 99 || bibittesATerre.get(i).getX() == canon.xDebutCanon - 101) {
                            bibittesATerre.get(i).empile = true;
                        } else if (!bibittesATerre.get(i).empile) {
                            int directionDuMouvement = 1;
                            bibittesATerre.get(i).rotation = -134;
                            if (bibittesATerre.get(i).getX() > canon.xDebutCanon - 100) {
                                directionDuMouvement = -1;
                                bibittesATerre.get(i).rotation = 134;
                            }
                            bibittesATerre.get(i).setLocation(bibittesATerre.get(i).getX() + directionDuMouvement, bibittesATerre.get(i).getY());
                        }
                    }
                } else {
                    for (int i = 0; i < bibittesATerre.size(); i++) {
                        bibittesATerre.get(i).setLocation(bibittesATerre.get(i).getX(), bibittesATerre.get(i).getY() - (i * 25));
                        bibittesATerre.get(i).rotation = -133;
                    }

                    animation = false;
                }

                repaint();
                try {
                    Thread.sleep(15);
                } catch (InterruptedException ex) {
                }


            }
        }
    };
    Timer tmrAcceleration = new Timer(1000, new ActionListener() {

        /**
         * Quand le power-up "augmentation de la difficulté" est activé, 
         * remet les fréquences d'apparition à leurs valeurs normales après
         * 10 secondes.
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            compteurTimer++;
            difficulteAugmente = true;

            if (compteurTimer == 5) {
                frequenceBibitte = (int) (frequenceBibitte * 2);
                frequenceOvni = (int) (frequenceOvni * 2);
                frequenceSuperOvni = (int) (frequenceSuperOvni * 2);
                compteurTimer = 0;
                puissanceEnAttente = null;
                difficulteAugmente = false;
                tmrAcceleration.stop();
            }
        }
    });
    Timer tmrTimeStop = new Timer(1000, new ActionListener() {

        @Override
        /**
         * Repart le thread du jeu 3 secondes après l'activation du power-up "arrêt du temps".
         */
        public void actionPerformed(ActionEvent e) {
            compteurTimer++;

            if (compteurTimer == 2) {
                jeu.pauseJeu();
                tmrTimeStop.stop();
                compteurTimer = 0;
            }

        }
    });// </editor-fold>

    /**
     * Constructeur du monde. Crée un canon, démare la gestion d'événements pour la souris et l'algorithme de difficulté.
     * @param jeu   Classe jeu dans laquelle le monde est créé.
     */
    public Monde(final Jeu jeu) {
        this.jeu = jeu;
        this.setLayout(null);
        this.setSize(1024, 768);
        this.add(canon);
        this.add(leCurseur);

        donnees.deserialiser();
        dernierAchievement = donnees.dernierAchievement;

        sourisBouge = new MouseMotionAdapter() {

            @Override
            public void mouseMoved(MouseEvent me) {
                bougerCanon(me);
                leCurseur.bouger(me.getX(),me.getY());
                

            }

            @Override
            public void mouseDragged(MouseEvent me) {
                bougerCanon(me);
                leCurseur.bouger(me.getX(), me.getY());
            }
        };

        sourisClique = new MouseAdapter() {

            @Override
            public void mousePressed(MouseEvent me) {

                if (sourisOn) {
                    if (me.getButton() == MouseEvent.BUTTON1) {
                        double angle;
                        double vX, vY;

                        angle = Math.atan(ySouris / xSouris);

                        if (angle > 0) {
                            angle = Math.PI - angle;
                        } else {
                            angle = Math.abs(angle);
                        }


                        vX = -vitesseProjectiles * Math.cos(angle);
                        vY = vitesseProjectiles * Math.sin(angle);

                        projectiles.add(new Projectile((int) xCibleCanon, (int) yCibleCanon, vX, vY, prototype));
                        add(projectiles.get(projectiles.size() - 1));

                        if (pointage > 0 && !jeu.getPaused()) {
                            points(-1);
                        }

                    } else if (me.getButton() == MouseEvent.BUTTON3) {
                        if (puissanceEnAttente != null && puissanceEnAttente.type != TypePowerUp.NEGATIF_ACCELERATION) {

                            activerPuissance();
                            puissanceEnAttente = null;
                        }

                    }
                }
            }
        };
        
        this.addMouseListener(sourisClique);
        this.addMouseMotionListener(sourisBouge);
        
        
        setCurseur();

        algorithmeDeDifficulte.start();


    }

    /**
     * Dessine un fond noir.
     * @param g permet de dessiner le fond.
     */
    @Override
    public void paintComponent(Graphics g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, getWidth(), getHeight());

    }

    // <editor-fold defaultstate="collapsed" desc="Création d'éléments du jeu">
    /**
     * Crée des ovnis et des super-ovnis aléatoirement à une hauteur aléatoire,
     * selon une fréquence d'apparition.
     */
    public void spawnOvnis() {

        int spawnOvnis = rnd.nextInt(frequenceOvni);
        int hauteur = rnd.nextInt(200);
        int spawnSuperOvnis = rnd.nextInt(frequenceSuperOvni);

        if (spawnOvnis == 1) {
            ovnis.add(new Ovni(0, hauteur, prototype));
            add(ovnis.get(ovnis.size() - 1));
        }
        if (spawnSuperOvnis == 1) {
            ovnis.add(new SuperOvni(getWidth(), hauteur, prototype));
            add(ovnis.get(ovnis.size() - 1));
        }

    }

    /**
     * Crée des des devoirs de maths/pacman/bibittes aléatoirement à partir de la position
     * d'un ovni dans le monde, selon une fréquence d'apparition.
     * Crée aussi un obus lorsqu'un super-ovni se situe au dessus du canon.
     */
    public void spawnBibittes() {

        int spawnBibitte = rnd.nextInt(frequenceBibitte);
        int choixOvnis;

        if (spawnBibitte == 1) {
            if (!ovnis.isEmpty()) {
                choixOvnis = rnd.nextInt(ovnis.size());
                if (!(ovnis.get(choixOvnis) instanceof SuperOvni)) {
                    bibittes.add(new Bibitte(ovnis.get(choixOvnis).getX(), ovnis.get(choixOvnis).getY(), prototype));
                    add(bibittes.get(bibittes.size() - 1));
                }

            }

        }
        for (int i = 0; i < ovnis.size(); i++) {
            if (ovnis.get(i) instanceof SuperOvni) {
                if (ovnis.get(i).getX() > canon.xDebutCanon - 5 && ovnis.get(i).getX() < canon.xDebutCanon + 5) {
                    if (!ovnis.get(i).obusLargue) {
                        obus.add(new Obus(ovnis.get(i).getX(), ovnis.get(i).getY(), prototype));
                        add(obus.get(obus.size() - 1));
                        ovnis.get(i).obusLargue = true;
                    }
                }
            }
        }
    }

    /**
     * Crée des power-ups lorsqu'un certain nombre d'ennemis est tué à une position en x aléatoire,
     * selon une fréquence d'apparition et choisit le type de power-up selon des fréquences également.
     */
    public void spawnPowerUps() {
        int spawnX = rnd.nextInt(getWidth() - 30);
        int typePowerUp = rnd.nextInt(85);
        TypePowerUp type = null;


        if (killCount % moduloPowerUp == 0 && testPowerUp) {
            testPowerUp = false;

            if (typePowerUp < TypePowerUp.ARRET_DU_TEMPS.getChance()) {
                type = TypePowerUp.ARRET_DU_TEMPS;
            } else if (typePowerUp < TypePowerUp.NUKE.getChance()) {
                type = TypePowerUp.NUKE;
            } else if (typePowerUp < TypePowerUp.TETE_CHERCHEUSE.getChance()) {
                type = TypePowerUp.TETE_CHERCHEUSE;
            } else if (typePowerUp < TypePowerUp.NEGATIF_ACCELERATION.getChance() && !difficulteAugmente) {
                type = TypePowerUp.NEGATIF_ACCELERATION;
            } else {
                type = TypePowerUp.ARRET_DU_TEMPS;
            }

            powerUps.add(new PowerUp(type, spawnX, this, prototype));
            add(powerUps.get(powerUps.size() - 1));

        } else if (killCount % moduloPowerUp != 0) {
            testPowerUp = true;
        }
    }

    /**
     * Lorsque le power-up "missiles à tête chercheuse" est activé, crée une tourelle
     * qui tire automatiquement.
     */
    public void ajoutTourelle() {
        tourelles.add(new Tourelle(this, prototype));
        add(tourelles.get(tourelles.size() - 1));
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Power-ups">
    /**
     * Oriente automatiquement le canon des tourelles vers leur prochaine cible.
     * Utilise le même principe que le canon, mais avec la position de l'ennemi
     * au lieu de celle de la souris.
     */
    
    
    public synchronized void tirTourelles(Tourelle tourelle) {


        if (cibles.contains(tourelle.laCible)) {


            double angle = Math.atan(((double) tourelle.orientationY - (double) tourelle.laCible.getY()) / ((double) tourelle.laCible.getX()) - (double) tourelle.orientationX);

            if (angle > 0) {
                angle = Math.PI - angle;
            } else {
                angle = Math.abs(angle);
            }

            projectiles.add(new Projectile(tourelle.orientationX, tourelle.orientationY, 0, 0, prototype));
            projectiles.get(projectiles.size() - 1).estUnProjectileDeTourelle = true;
            projectiles.get(projectiles.size() - 1).laCible = tourelle.laCible;
            add(projectiles.get(projectiles.size() - 1));

            tourelle.projectileTire = projectiles.get(projectiles.size() - 1);

        }

    }

    /**
     * Lorsque le power-up "augmentation de la difficulté" est touché, augmente la
     * fréquence d'apparition des ennemis et active un timer qui enlève les
     * effets du power-up après 10 secondes.
     */
    public void accelererCibles() {

        if(frequenceBibitte>12&&frequenceOvni>12&&frequenceOvni>12) {
        frequenceBibitte = (int) (frequenceBibitte / 2);
        frequenceOvni = (int) (frequenceOvni / 2);
        frequenceSuperOvni = (int) (frequenceSuperOvni / 2);

        tmrAcceleration.start();
        }

    }

    /**
     * Appèle la méthode qui active l'effet du power-up qui est utilisé.
     */
    public void activerPuissance() {
        try {
            puissanceEnAttente.activerPuissance();
        } catch (InterruptedException ex) {
        }
        powerUpsUtilise++;
    }

    /**
     * Arrête le thread du jeu pour un temps limité lorsque le power-up
     * "arrêt du temps" est activé.
     */
    public void timeStop() {
        jeu.pauseJeu();
        if (difficulteAugmente) {
            tmrAcceleration.setDelay(3);
        }
        tmrTimeStop.start();
    }

    /**
     * Lorsque le power-up "nuke" ou "gentille bombe themonucléaire" est activé,
     * élimine tous les ennemis du monde et génère des explosions à leur mort.
     */
    public void genocide() {

        explosions.add(new Explosion(getWidth() / 4, getHeight() / 4, getWidth() / 2, getHeight() / 2, 5));
        add(explosions.get(explosions.size() - 1));

        
        for (int j = 0; j < bibittes.size(); j++) {
            if (!bibittes.get(j).aTerre) {
                aEnlever.add(bibittes.get(j));
                explosions.add(new Explosion(bibittes.get(j).getX() - bibittes.get(j).getWidth(), bibittes.get(j).getY() - bibittes.get(j).getHeight() / 2, 10 * bibittes.get(j).getWidth(), 2 * bibittes.get(j).getHeight(), 10));
                add(explosions.get(explosions.size() - 1));
                points(2);
                donnees.nbBibittes++;
                if (donnees.nbBibittes > 100) {
                    dernierAchievement = "Nuke";
                }
            }
        }
        for (int k = 0; k < obus.size(); k++) {
            aEnlever.add(obus.get(k));
            explosions.add(new Explosion(obus.get(k).getX() - obus.get(k).getWidth(), obus.get(k).getY() - obus.get(k).getHeight() / 2, 5 * obus.get(k).getWidth(), 2 * obus.get(k).getHeight(), 2));
            add(explosions.get(explosions.size() - 1));
            points(25);
        }


        for (int i = 0; i < ovnis.size(); i++) {
            aEnlever.add(ovnis.get(i));
            explosions.add(new Explosion(ovnis.get(i).getX() - ovnis.get(i).getWidth() / 3, ovnis.get(i).getY() - ovnis.get(i).getHeight(), 4 * ovnis.get(i).getWidth(), 3 * ovnis.get(i).getHeight(), 2));
            add(explosions.get(explosions.size() - 1));
            if (ovnis.get(i) instanceof SuperOvni) {
                points(10);
            } else {
                points(5);
            }
            donnees.nbOvnis++;
            if (donnees.nbOvnis == 200) {
                dernierAchievement = "Mucho violento";
            }
        }

        

    }

    /**
     * Appèle les méthodes nécessaires pour déterminer l'emplacement
     * du bout du canon selon l'emplacement du curseur.
     * @param me permet d'obtenir la position de la souris.
     */
    /**
     * Retourne le nom du power-up que le joueur peut utiliser.
     * @return type du power up
     */
    public String getNomPowerUp() {
        if (puissanceEnAttente != null) {
            return puissanceEnAttente.nom;
        } else {
            return "";
        }
    }// </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Mouvements, collisions et modes d'affichages">
    public void bougerTourelles() {
        cibles.clear();

        cibles.addAll(obus);
        cibles.addAll(ovnis);

        for (int bibitte = 0; bibitte < bibittes.size(); bibitte++) {
            if (!bibittes.get(bibitte).aTerre) {
                cibles.add(bibittes.get(bibitte));
            }
        }

        for (int uneTourelle = 0; uneTourelle < tourelles.size(); uneTourelle++) {

            if (!cibles.contains(tourelles.get(uneTourelle).laCible)) {
                tourelles.get(uneTourelle).laCible = null;
            }

            if (tourelles.get(uneTourelle).laCible == null) {
                int cibleTourelle = rnd.nextInt(cibles.size());
                tourelles.get(uneTourelle).laCible = cibles.get(cibleTourelle);
                int xCible = tourelles.get(uneTourelle).laCible.getX() - tourelles.get(uneTourelle).xDebutCanon;
                int yCible = tourelles.get(uneTourelle).yDebutCanon - tourelles.get(uneTourelle).laCible.getY();

                double xCibleTourelle = tourelles.get(uneTourelle).xDebutCanon - deplacement.bougerCanon(xCible, yCible, longueurTourelles)[0];
                double yCibleTourelle = tourelles.get(uneTourelle).yDebutCanon - deplacement.bougerCanon(xCible, yCible, longueurTourelles)[1];
                tourelles.get(uneTourelle).bouger(xCibleTourelle, yCibleTourelle);
            } else {
                int xCible = tourelles.get(uneTourelle).laCible.getX() - tourelles.get(uneTourelle).xDebutCanon;
                int yCible = tourelles.get(uneTourelle).yDebutCanon - tourelles.get(uneTourelle).laCible.getY();

                double xCibleTourelle = tourelles.get(uneTourelle).xDebutCanon - deplacement.bougerCanon(xCible, yCible, longueurTourelles)[0];
                double yCibleTourelle = tourelles.get(uneTourelle).yDebutCanon - deplacement.bougerCanon(xCible, yCible, longueurTourelles)[1];
                tourelles.get(uneTourelle).bouger(xCibleTourelle, yCibleTourelle);
            }


        }

    }

    /**
     * Lorsqu'une cible est disponible dans le monde, tire un missile à tête chercheuse
     * vers cette cible à partir d'une tourelle
     * @param tourelle la tourelle utilisée pour tirer
     */
    public void bougerCanon(MouseEvent me) {
        xSouris = me.getX() - canon.xDebutCanon;

        ySouris = canon.yDebutCanon - me.getY();
        

        xCibleCanon = canon.xDebutCanon - deplacement.bougerCanon(xSouris, ySouris, longeurCanon)[0];
        yCibleCanon = canon.yDebutCanon - deplacement.bougerCanon(xSouris, ySouris, longeurCanon)[1];
        canon.bouger(xCibleCanon, yCibleCanon);
    }

    /**
     * Parcourt les listes d'éléments présents dans le monde et les fait se déplacer 
     * selon leur vitesse respective (en x et en y). Ajoute un élément à la liste
     * d'éléments à enlever lorsqu'il sort des limites du monde.
     * Termine la partie lorsqu'un obus ou une bibitte tombe sur le canon.
     */
    public void bougerElements() {
        if (!ovnis.isEmpty()) {
            bougerTourelles();
        }
        
        for (int i = 0; i < projectiles.size(); i++) {

            if (projectiles.get(i).estUnProjectileDeTourelle) {
                double angle = 0;

                try {
                    angle = Math.atan(((double) projectiles.get(i).getY() - (double) projectiles.get(i).laCible.getY()) / (((double) projectiles.get(i).laCible.getX()) - (double) projectiles.get(i).getX()));
                } catch (Exception e) {
                }

                if (angle > 0) {
                    angle = Math.PI - angle;
                } else {
                    angle = Math.abs(angle);
                }


                projectiles.get(i).vitesseX = (int) (-vitesseProjectiles * Math.cos(angle));
                projectiles.get(i).vitesseY = (int) (vitesseProjectiles * Math.sin(angle));
                projectiles.get(i).bouger(0, 0);

            } else {
                projectiles.get(i).bouger(projectiles.get(i).vitesseY, projectiles.get(i).vitesseX);
                projectiles.get(i).vitesse(1);
            }
            if (projectiles.get(i).getY() > this.getHeight() || projectiles.get(i).getX() > this.getWidth() || projectiles.get(i).getX() < 0) {
                remove(projectiles.get(i));
                projectiles.remove(i);
            }

        }
        for (int i = 0; i < ovnis.size(); i++) {
            ovnis.get(i).bouger(ovnis.get(i).vitesse, 0);
            if (ovnis.get(i).getX() < -ovnis.get(i).getWidth() || ovnis.get(i).getX() > this.getWidth()) {
                remove(ovnis.get(i));
                ovnis.remove(i);
            }
        }

        for (int k = 0; k < bibittes.size(); k++) {
            if (bibittes.get(k).getY() > this.getHeight() - 2 * bibittes.get(k).getHeight()) {
                if (bibittes.get(k).getX() > (canon.xDebutCanon - 50) && bibittes.get(k).getX() < (canon.xDebutCanon + 50)) {
                    terminerPartie();
                }

                if (!bibittes.get(k).aTerre) {
                    compteurBibittes++;

                    bibittes.get(k).aTerre = true;



                    if (compteurBibittes == 4) {
                        terminerPartie();
                    }


                }
            } else {
                bibittes.get(k).bouger(bibittes.get(k).vitesse, 0);
            }
        }

        for (int j = 0; j < obus.size(); j++) {

            obus.get(j).bouger(obus.get(j).vitesse, 0);

            if (obus.get(j).getY() > this.getHeight() - obus.get(j).getHeight()) {

                terminerPartie();
            }
        }
        for (int p = 0; p < powerUps.size(); p++) {
            powerUps.get(p).bouger(0, 0);
            if (powerUps.get(p).getY() > getHeight()) {
                aEnlever.add(powerUps.get(p));
            }
        }
        

    }

    /**
     * Pacourt la liste des projectiles. Pour chaque projectile, parcourt chaque liste d'objec destructible
     * pour vérifier s'il entre en collision avec un de ces objets. Si oui, détruit les 2 objets et, selon 
     * le type d'objet détruit active l'effet ou augmente le pointage et le nombre d'ennemis tués.
     * Afin de détruire les objets impliqués dans les collisions, les mets dans une liste d'objets à 
     * enlever, qui est en suite parcourue pour enlever les objets du monde et le leurs listes respectives.
     * Génère des explosion lorsqu'un ennemi est touché.
     */
    public synchronized void detectionCollisions() {

        for (int projectile = 0; projectile < projectiles.size(); projectile++) {
            for (int ovni = 0; ovni < ovnis.size(); ovni++) {
                if (ovnis.get(ovni).getBounds().intersects(projectiles.get(projectile).getBounds())) {
                    for (int tourelle = 0; tourelle < tourelles.size(); tourelle++) {
                        if (ovnis.get(ovni) == tourelles.get(tourelle).laCible) {
                            tourelles.get(tourelle).laCible = null;
                        }
                    }
                    killCount++;
                    donnees.nbOvnis++;
                    if (donnees.nbOvnis == 200) {
                        dernierAchievement = "Mucho violento";
                    }
                    aEnlever.add(ovnis.get(ovni));
                    aEnlever.add(projectiles.get(projectile));
                    explosions.add(new Explosion(ovnis.get(ovni).getX() - ovnis.get(ovni).getWidth() / 4, ovnis.get(ovni).getY() - ovnis.get(ovni).getHeight(), 3 * ovnis.get(ovni).getWidth(), 3 * ovnis.get(ovni).getHeight(), 2));
                    add(explosions.get(explosions.size() - 1));

                    if (ovnis.get(ovni) instanceof SuperOvni) {
                        points(10);
                    } else {
                        points(5);
                    }

                }
            }
            for (int bibitte = 0; bibitte < bibittes.size(); bibitte++) {
                if (bibittes.get(bibitte).getBounds().intersects(projectiles.get(projectile).getBounds())) {
                    for (int tourelle = 0; tourelle < tourelles.size(); tourelle++) {
                        if (bibittes.get(bibitte) == tourelles.get(tourelle).laCible) {
                            tourelles.get(tourelle).laCible = null;
                        }
                    }

                    if (!bibittes.get(bibitte).aTerre) {
                        aEnlever.add(bibittes.get(bibitte));
                        killMineurs++;
                        explosions.add(new Explosion(bibittes.get(bibitte).getX() - bibittes.get(bibitte).getWidth(), bibittes.get(bibitte).getY() - bibittes.get(bibitte).getHeight() / 2, 5 * bibittes.get(bibitte).getWidth(), 2 * bibittes.get(bibitte).getHeight(), 2));
                        add(explosions.get(explosions.size() - 1));

                        donnees.nbBibittes++;
                        if (donnees.nbBibittes == 100) {
                            dernierAchievement = "Nuke";
                        }
                        points(2);
                    }
                    aEnlever.add(projectiles.get(projectile));
                }
            }
            for (int unObus = 0; unObus < obus.size(); unObus++) {
                if (obus.get(unObus).getBounds().intersects(projectiles.get(projectile).getBounds())) {
                    for (int tourelle = 0; tourelle < tourelles.size(); tourelle++) {
                        if (obus.get(unObus) == tourelles.get(tourelle).laCible) {
                            tourelles.get(tourelle).laCible = null;
                        }
                    }
                    killMineurs++;
                    aEnlever.add(obus.get(unObus));
                    aEnlever.add(projectiles.get(projectile));
                    explosions.add(new Explosion(obus.get(unObus).getX() - obus.get(unObus).getWidth(), obus.get(unObus).getY() - obus.get(unObus).getHeight() / 2, obus.get(unObus).getWidth() * 10, obus.get(unObus).getHeight(), 2));
                    add(explosions.get(explosions.size() - 1));
                    points(25);
                }
            }
            for (int unPowerUp = 0; unPowerUp < powerUps.size(); unPowerUp++) {
                if (powerUps.get(unPowerUp).getBounds().intersects(projectiles.get(projectile).getBounds()) && !projectiles.get(projectile).estUnProjectileDeTourelle) {
                    puissanceEnAttente = powerUps.get(unPowerUp);
                    aEnlever.add(powerUps.get(unPowerUp));
                    aEnlever.add(projectiles.get(projectile));

                    if (puissanceEnAttente.type == TypePowerUp.NEGATIF_ACCELERATION) {

                        try {
                            puissanceEnAttente.activerPuissance();
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            }
        }

        for (int i = 0; i < aEnlever.size(); i++) {
            remove(aEnlever.get(i));
        }



        obus.removeAll(aEnlever);
        ovnis.removeAll(aEnlever);
        bibittes.removeAll(aEnlever);
        projectiles.removeAll(aEnlever);
        powerUps.removeAll(aEnlever);
        try{
        explosions.removeAll(aEnlever);
        } catch(Exception e){}
        aEnlever.clear();


    }

    /**
     * Lorsque le mode prototype est activé, change l'apparence des bibittes 
     * présentes dans le jeu et du canon.
     */
    public void modePrototype() {
        for (int i = 0; i < bibittes.size(); i++) {

            Bibitte bebitte = new Bibitte(bibittes.get(i).getX(),
                    bibittes.get(i).getY(), prototype);

            remove(bibittes.get(i));
            bibittes.remove(i);
            bibittes.add(bebitte);
            add(bebitte);
        }

        canon.prototype = prototype;
        canon.repaint();
    }

    /**
     * Retourne les points amassés par le joueur.
     * @return pointage
     */// </editor-fold>
    
    public int getPointage() {
        return pointage;
    }

    /**
     * Lorsqu'une nouvelle partie est commencée, remet tout à zéro: le pointage, le 
     * power-up utilisable et les éléments dans le monde.
     */
    public void toutEnlever() {

        pointage = 0;
        puissanceEnAttente = null;
        compteurBibittes = 0;
        killCount = 1;
        
        
         frequenceBibitte =  frequenceBibitteDefault;
         frequenceOvni =  frequenceOvniDefault;
         frequenceSuperOvni =  frequenceSuperOvniDefault;
         moduloPowerUp =  moduloPowerUpDefault;
         
         algorithmeDeDifficulte.compteur = 0;


        aEnlever.addAll(obus);
        aEnlever.addAll(ovnis);
        aEnlever.addAll(bibittes);
        aEnlever.addAll(projectiles);
        aEnlever.addAll(powerUps);
        aEnlever.addAll(explosions);
        aEnlever.addAll(tourelles);


        for (int i = 0; i < aEnlever.size(); i++) {
            remove(aEnlever.get(i));
        }


        obus.removeAll(aEnlever);
        ovnis.removeAll(aEnlever);
        bibittes.removeAll(aEnlever);
        projectiles.removeAll(aEnlever);
        powerUps.removeAll(aEnlever);
        explosions.removeAll(aEnlever);
        tourelles.removeAll(tourelles);
        aEnlever.clear();

        partieTerminee = false;
    }

    /**
     * Quand le joueur perd la partie, arrête le thread du jeu et active l'animation de fin de partie.
     * Affiche le pointage et demande au joueur de signer un nom de 3 lettres.
     * Active la sérialisation des données des achievements et des pointage.
     */
    public void terminerPartie() {
        jeu.pauseJeu();
        partieTerminee = true;
        if (animationCommence) {
            animation = true;
        } else {
            animation = true;
            animationCommence = true;
            animationDeFin.start();
        }

        donnees.nbParties++;

        if (donnees.nbParties == 1) {
            dernierAchievement = "n00b";
        } else if (donnees.nbParties == 10) {
            dernierAchievement = "Own";
        } else if (donnees.nbParties == 1000) {
            dernierAchievement = "Addict";
        }

        String nom = "";

        try {
            while (nom.length() != 3) {
                nom = JOptionPane.showInputDialog(this, "Partie terminée. Vous avez obtenu " + pointage + " points.\nVeuillez entrer votre nom composé de 3 lettres.", "Fin de la partie", JOptionPane.QUESTION_MESSAGE);
            }
        } catch (NullPointerException e) {
        }

        donnees.nouveauScore(pointage, nom);

        if (pointage == 0) {
            donnees.bazingaObtenu = true;
            dernierAchievement = "Bazinga!";
        }

        donnees.dernierAchievement = dernierAchievement;
        donnees.serialiser();
    }

    /**
     * Met le jeu à pause et affiche un JOptionPane contenant le tableau des
     * pointages.
     */
    public void afficherScores() {
        if (!jeu.getPaused()) {
            jeu.pauseJeu();
        }
        donnees.afficherScores();
        if (!partieTerminee) {
            jeu.pauseJeu();
        }
    }

    /**
     * Met le jeu à pause et affiche un JOptionPane contenant les trophées.
     */
    public void afficherAchievements() {
        if (!jeu.getPaused()) {
            jeu.pauseJeu();
        }
        donnees.afficherTrophees();
        if (!partieTerminee) {
            jeu.pauseJeu();
        }
    }

    /**
     * Lorsque le joueur obtient des points, augmente les points dans la classe
     * données pour les trophés.
     * @param pts points obtenus par le joueur 
     */
    public void points(int pts) {
        pointage += pts;
        donnees.nbPoints += pts;
        if (donnees.nbPoints >= 2500 && donnees.nbPoints <= 2525) {
            dernierAchievement = "Pro";
        } else if (donnees.nbPoints >= 10000 && donnees.nbPoints <= 10025) {
            dernierAchievement = "1337";
        }
    }

    /**
     * Pour l'affichage du dernier trophée obtenu dans l'interface de droite.
     * @return nom du dernier trophée obtenu
     */
    public String dernierAchievement() {

        return dernierAchievement;
    }
    
    /**
     * 
     * @return Renvoit le niveau de difficulté string
     */
    public String getNiveauDif() {
        return algorithmeDeDifficulte.getNiveauDeDifficulte();
    }
    /**
     * 
     * @return Renvoit le niveau de difficulté int
     */
    public int getCompteurDif() {
        return algorithmeDeDifficulte.getCompteur();
    }
    /**
     * Enleve le curseur habituel
     */
    private void setCurseur() {
        byte[] imageByte = new byte[0];
        Cursor myCursor;
        Point myPoint = new Point(0, 0);

        Image cursorImage = Toolkit.getDefaultToolkit().createImage(imageByte);
        myCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, myPoint, "cursor");

        setCursor(myCursor);
    }
    
}
